package uk.gov.swmprobation.cpvisibility;

import java.util.List;

import android.app.AlarmManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.IBinder;
import android.provider.Settings;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log;

import com.ibm.mqtt.IMqttClient;
import com.ibm.mqtt.MqttAdvancedCallback;
import com.ibm.mqtt.MqttClient;
import com.ibm.mqtt.MqttException;
import com.ibm.mqtt.MqttNotConnectedException;
import com.ibm.mqtt.MqttPersistenceException;

public class MQTTConnection extends Service implements MqttAdvancedCallback{

	//Location where broker is running
	private static final String HOST = "test.mosquitto.org";
	private static final String PORT = "1883";
	//30 minute keep-alive ping
	private static final short KEEP_ALIVE = 60 * 30;
	//Unique identifier of this device
	private static String CLIENT_ID;
	private List<List<Integer>> unsubscribed;
	
    private static final String ACTION_KEEPALIVE ="com.examples.pushclient.ACTION_KEEPALIVE";
    private IMqttClient mClient;
    private AlarmManager mManager;
    private PendingIntent alarmIntent;

	private boolean listenerRegistered = false;
    
	public void onCreate(){
		super.onCreate();
		
		DatabaseHandler.getInstance(this);
		CLIENT_ID =  Settings.Secure.getString(getContentResolver(),Settings.Secure.ANDROID_ID);
		mManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
		Intent intent = new Intent(ACTION_KEEPALIVE);
		alarmIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
	       registerReceiver(mReceiver, new IntentFilter(ACTION_KEEPALIVE));
	       try {
	           //Format: tcp://hostname@port
	           String connectionString = String.format("%s%s@%s", MqttClient.TCP_ID, HOST,
	PORT);
	           mClient = MqttClient.createMqttClient(connectionString, null);
	       } catch (MqttException e) {
	           e.printStackTrace();
	           //Can't continue without a client
	           stopSelf();
	       }
	}

	@Override
	public void onStart(Intent intent, int startId) {
	    //Callback on Android devices prior to 2.0
		listenForNetwork();
	}
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
	    //Callback on Android devices 2.0 and later
		listenForNetwork();
	    //If Android kills this service, we want it back when possible
	    return START_STICKY;
	}

	public static Thread performOnBackgroundThread(final Runnable runnable) {
	    final Thread t = new Thread() {
	        @Override
	        public void run() {
	            try {
	                runnable.run();
	            } finally {

	            }
	        }
	    };
	    t.start();
	    return t;
	}
	
	private Runnable runCommand = new Runnable(){

		@Override
		public void run() {
			try {
		        //Make a connection
		        mClient.connect(CLIENT_ID, true, KEEP_ALIVE);
		        //Target MQTT callbacks here
		        mClient.registerSimpleHandler(MQTTConnection.this);
		        //Subscribe to any needed topics
				unsubscribed = DatabaseHandler.getUnsubscribed();
				
				int n = unsubscribed.size();
				String[] topics = new String[n];
				int[] qos = new int[n];
				for (int i=0;i<n;i++){
					if(Integer.valueOf(unsubscribed.get(i).get(1))==Project.TYPE_MINE){
						topics[i]=unsubscribed.get(i).get(0)+"/#";
					} else {
						topics[i]=unsubscribed.get(i).get(0)+"/status";
					}
					qos[i]=2;
				}
				if(n>0){
					mClient.subscribe(topics, qos);
				}
		        //Schedule a ping
		        scheduleKeepAlive();

		        if (listenerRegistered) {
			        unregisterReceiver(networkListener);
			        listenerRegistered = false;
		        }

		    } catch (MqttException e) {
		        e.printStackTrace();
		    }
			
		}
		
	};

	@Override
	public void onDestroy() {
	    super.onDestroy();
	    if (listenerRegistered) {
	        unregisterReceiver(networkListener);
	        listenerRegistered = false;
        }
	    unscheduleKeepAlive();
	    if(mClient != null) {
	        try {
	            mClient.disconnect();
	            mClient.terminate();
	        } catch (MqttPersistenceException e) {
	            e.printStackTrace();
	        }
	        mClient = null;
	      }
	    }
	
	//Handle ping alarms to keep the connection alive
	private BroadcastReceiver mReceiver = new BroadcastReceiver() {
	    @Override
	    public void onReceive(Context context, Intent intent) {
	        if(mClient == null) {
	            return;
	        }
	        //Ping the MQTT service
	        try {
	            mClient.ping();
	        } catch (MqttException e) {
	            e.printStackTrace();
	        }
	        //Schedule the next alarm
	        scheduleKeepAlive();
	    }
	};

	private void scheduleKeepAlive() {
	    long nextWakeup = System.currentTimeMillis() + (KEEP_ALIVE * 1000);
	    mManager.set(AlarmManager.RTC_WAKEUP, nextWakeup, alarmIntent);
	}
	private void unscheduleKeepAlive() {
	    mManager.cancel(alarmIntent);
	}
	
	BroadcastReceiver networkListener = new BroadcastReceiver(){
		@Override
		public void onReceive(Context context, Intent intent) {
			if(isNetworkAvailable()){
				performOnBackgroundThread(runCommand);
			}
		}
	};
	
	private boolean isNetworkAvailable() {
	    ConnectivityManager connectivityManager 
	          = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
	    NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
	    return activeNetworkInfo != null && activeNetworkInfo.isConnected();
	}
	
	public void listenForNetwork(){
		IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);        
		registerReceiver(networkListener, filter);
	}
	
	private void createNotification(String projectid,String status){
		int intProjectid = Integer.valueOf(projectid);
		NotificationCompat.Builder mBuilder =
				new NotificationCompat.Builder(this)
				.setSmallIcon(R.drawable.ic_launcher)
				.setContentTitle("Project Updated")
				.setContentText(Helper.shortName(DatabaseHandler.projectName(projectid))+" status changed to "+status);
				//Creates an explicit intent for an Activity in your app
				Intent resultIntent = new Intent(this, ProjectReview.class);
				resultIntent.putExtra("project_id", intProjectid);
				resultIntent.putExtra("updated", true);

				//The stack builder object will contain an artificial back stack for the
				//started Activity.
				//This ensures that navigating backward from the Activity leads out of
				//your application to the Home screen.
				TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
				//Adds the back stack for the Intent (but not the Intent itself)
				stackBuilder.addParentStack(MainActivity.class);
				//Adds the Intent that starts the Activity to the top of the stack
				stackBuilder.addNextIntent(resultIntent);
				PendingIntent resultPendingIntent =
				stackBuilder.getPendingIntent(
				    0,
				    PendingIntent.FLAG_UPDATE_CURRENT
				);
				mBuilder.setContentIntent(resultPendingIntent);
				NotificationManager mNotificationManager =
				(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
				//mId allows you to update the notification later on.
				mNotificationManager.notify(intProjectid, mBuilder.build());
	}

    /* MqttSimpleCallback Methods */
    @Override
    public void connectionLost() throws Exception {
        //mClient.terminate();
        //mClient = null;
        listenForNetwork();
    }
    @Override
    public void publishArrived(String topicName, byte[] payload, int qos, boolean retained) throws Exception {
	    	String status = new String(payload);
			String projectid=topicName.substring(0,topicName.indexOf("/"));
			DatabaseHandler.flagUpdated(Integer.valueOf(projectid), status);
			createNotification(projectid, status);
    	}
    
    @Override
    public void subscribed(int msgId, byte[] grantedQoS){
    	Log.e("MQTT","Subscribed");
    	DatabaseHandler.confirmSubscribed(unsubscribed);
    }
    
    public void destroyService(){
    	if(mReceiver != null){
    		unregisterReceiver(mReceiver);
    	}
	    unscheduleKeepAlive();
	    if(mClient != null) {
	        try {
	            mClient.disconnect();
	            mClient.terminate();
	        } catch (MqttPersistenceException e) {
	            e.printStackTrace();
	        }
	        mClient = null;
	      }

    }
    
	@Override
	public IBinder onBind(Intent arg0) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void published(int arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void unsubscribed(int arg0) {
		// TODO Auto-generated method stub
		
	}

	public void subscribenow(){
		if(mClient!=null){
			//Subscribe to any needed topics
			unsubscribed = DatabaseHandler.getUnsubscribed();
			
			int n = unsubscribed.size();
			String[] topics = new String[n];
			int[] qos = new int[n];
			for (int i=0;i<n;i++){
				if(Integer.valueOf(unsubscribed.get(i).get(1))==Project.TYPE_MINE){
					topics[i]=unsubscribed.get(i).get(0)+"/#";
				} else {
					topics[i]=unsubscribed.get(i).get(0)+"/status";
				}
				qos[i]=2;
			}
			if(n>0){
				try {
					mClient.subscribe(topics, qos);
				} catch (MqttNotConnectedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (MqttException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
	        //Schedule a ping
	        scheduleKeepAlive();
		}
	}
}